package hichecker.ui.components.custom_table;

import hichecker.ui.components.custom_table.column_class.Grade;
import hichecker.ui.components.custom_table.column_class.Points;
import hichecker.ui.components.custom_table.row_sorter.TableRowSorterNullsLast;
import hichecker.ui.components.custom_table.table_cell_editor.AbstractTableCellEditor;
import hichecker.ui.components.custom_table.table_cell_editor.CustomTableCellEditor;
import hichecker.ui.components.custom_table.table_cell_editor.GradeEditor;
import hichecker.ui.components.custom_table.table_cell_editor.LongNumberEditor;
import hichecker.ui.components.custom_table.table_cell_editor.PointsEditor;
import hichecker.ui.components.custom_table.table_cell_renderer.BooleanTableCellRenderer;
import hichecker.ui.components.custom_table.table_cell_renderer.ConflictTypeTableCellRenderer;
import hichecker.ui.components.custom_table.table_cell_renderer.CustomTableCellRenderer;
import hichecker.ui.custom_views.conflict_handling.ConflictImportedTableModel.ConflictType;

import java.awt.Component;
import java.util.Arrays;

import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;


public class CustomTable extends JTable{

	/* DECLARATION ****************************************************************************** */
	/**
	 * TableModel
	 */
	private AbstractCustomTableModel<?> model;

	/* CONSTRUCTOR ***************************************************************************** */
	/**
	 * Erzeugt die Tabelle, das TableModel, den CellRenderer und den RowSorter der Tabelle
	 */
	public CustomTable(AbstractCustomTableModel<?> model) {
		this(model, 0);
	}


	public CustomTable(AbstractCustomTableModel<?> model, int width) {
		// Model
		super(model);
		this.model = model;

		// Set editors and renderers
		setDefaultEditors();
		setDefaultRenderers();
		setDefaultHeaderRenderers();

		// Activate autoresizing
		autoResizeColumns();
		setAutoResizeable(width);


		// Disable column movement
		getTableHeader().setReorderingAllowed(false);

		TableRowSorterNullsLast<TableModel> sorter = new TableRowSorterNullsLast<TableModel>(model);
		sorter.setSortsOnUpdates(true);
		setRowSorter(sorter);

		// Update model data
		model.updateDefaultData();
	}

	/**
	 * Sets the cell editors per column class
	 */
	private void setDefaultEditors() {
		setDefaultEditor(Object.class, new CustomTableCellEditor());
		setDefaultEditor(Grade.class, new GradeEditor());
		setDefaultEditor(Points.class, new PointsEditor());
		setDefaultEditor(Integer.class, new LongNumberEditor());
	}

	/**
	 * Sets the cell renderer per column class
	 */
	private void setDefaultRenderers() {
		setDefaultRenderer(Boolean.class, new BooleanTableCellRenderer());
		setDefaultRenderer(ConflictType.class, new ConflictTypeTableCellRenderer());
		setDefaultRenderer(Object.class, new CustomTableCellRenderer());
	}

	/**
	 * Sets the renderer for the table header and adds header values
	 */
	private void setDefaultHeaderRenderers() {
		// Add header values as present in the model
		TableColumnModel tcm = getColumnModel();
		Object[] headerValues = model.getHeaderValues();
		//MultiLineHeaderRenderer headerRenderer = new MultiLineHeaderRenderer(getTableHeader().getDefaultRenderer(), JLabel.CENTER, JLabel.BOTTOM);
		for (int i = 0; i < headerValues.length; i++) {
			//tcm.getColumn(i).setHeaderRenderer(headerRenderer);
			tcm.getColumn(i).setHeaderValue(headerValues[i]);
		}
	}


	/**
	 * Sets auto resize of the table, depending on given width. If preferred width is less than
	 * given width, the table would be resized to fit into given space. Otherwise the table would
	 * stay in prefered size, scrollbars will appear.
	 *
	 * @param width
	 *            available width for the table
	 */
	public void setAutoResizeable(int width) {
		int tableWidth = getPreferredSize().width;
		int mode = tableWidth <= width ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF;
		setAutoResizeMode(mode);
	}

	private void autoResizeColumns() {
		// Für jede Spalte optimale Breite ermitteln
		DefaultTableColumnModel colModel = (DefaultTableColumnModel) getColumnModel();
		for (int i = 0; i < getColumnCount(); i++) {
			TableColumn column = colModel.getColumn(i);

			// Renderer ermitteln
			TableCellRenderer renderer = column.getHeaderRenderer();
            if (renderer == null)
            	// Beim initialisieren gibt's kein TableHeader => raus hier
            	if (getTableHeader() != null)
            		renderer = getTableHeader().getDefaultRenderer();
            	else
            		return;

			// Kopfbreite ermitteln
			Component header = renderer.getTableCellRendererComponent(this,
					column.getHeaderValue(), false, false, 0, 0);
			int width = header.getPreferredSize().width;

			// Maximale Datenbreite ermitteln
			for (int row = 0; row < getRowCount(); row++) {
				Component cell = getCellRenderer(row, i).getTableCellRendererComponent(this,
						getValueAt(row, i), false, false, row, i);

				width = Math.max(width, cell.getPreferredSize().width);
			}

			// Rand hinzufügen
			width += 10;

			// Breite setzen
			column.setPreferredWidth(width);
		}
	}

	/**
	 * Indicates whether the currently edited cell is valid or not. Returns {@code true} if the inputs
	 * validation is irrelevant (which is the case if the TableCellEditor is not an instance of
	 * {@link AbstractTableCellEditor}) or the input is valid (which is when
	 * {@link AbstractTableCellEditor#isValidInput()} returns {@code true})
	 *
	 * @return
	 */
	public boolean isCorrect() {
		TableCellEditor editor = getCellEditor();
		if (editor == null) return true;

		if (editor instanceof AbstractTableCellEditor)
			return ((AbstractTableCellEditor) editor).isValidInput();

		return true;
	}

	/**
	 * Sets the currently edited value. Should only be called when editing was stopped by an
	 * unexpected event.
	 */
	public void setCurrentValue() {
		TableCellEditor editor = getCellEditor();
		if (editor == null) return;
		model.setValueAt(editor.getCellEditorValue(), getEditingRow(), getEditingColumn());
	}

	/**
	 * Ruft zuerst die implementierte Methode {@link JTable#tableChanged} und dann die Methode
	 * {@link #autoResizeColumns()}
	 */
	@Override
	public void tableChanged(TableModelEvent e) {
		super.tableChanged(e);
		autoResizeColumns();
	}

	public int getSelectedModelRow() {
		int tableRow = getSelectedRow();
		int modelRow = convertRowIndexToModel(tableRow);
		return modelRow;
	}

	public int[] getSelectedModelRows() {
		int[] tableRows = super.getSelectedRows();
		int[] modelRows = new int[tableRows.length];
		for (int i = 0; i < tableRows.length; i++)
			modelRows[i] = convertRowIndexToModel(tableRows[i]);
		Arrays.sort(modelRows);

		return modelRows;
	}

}